package com.ruoyi.web.server.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.vo.CommonVO;
import com.ruoyi.common.exception.ProcessStopException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ExecUtil;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.df.domain.DensityFusionParam;
import com.ruoyi.df.domain.DensityFustionSatelliteVO;
import com.ruoyi.df.domain.DensityFustionVO;
import com.ruoyi.df.service.IFusionDenDataService;
import com.ruoyi.df.service.IFusionSatelliteRelService;
import com.ruoyi.system.domain.ResultSave;
import com.ruoyi.system.service.IResultSaveService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.rmi.ServerException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.ruoyi.common.utils.biz.ResultSaveModule.DATA_FUSION;
import static com.ruoyi.common.utils.biz.ResultSaveModule.DATA_FUSION_GRID;

/**
 * 大气密度数据融合
 */
@Service
@Slf4j
public class DensityDataFusionService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    IResultSaveService resultSaveService;

    @Autowired
    IFusionSatelliteRelService relService;

    @Autowired
    IFusionDenDataService fusionService;

    /**
     * 下载融合的结果
     * @param param
     * @return
     */
    public File download(DensityFusionParam param) throws Exception {
        String key = "FUSION_RES:"+param.toString();
        String cachStr = (String)redisTemplate.opsForValue().get(key);
        if (null == cachStr) {
            this.fusionCalc(param);
            cachStr = (String)redisTemplate.opsForValue().get(key);
        }
        List<String> strings = JSONUtil.toList(cachStr, String.class);
        List<String> res = new ArrayList<>();
        for (String line : strings) {
            if (line.startsWith("###")) {
                String[] split = line.split("['#,]+");
                ///dgdq/tmp/dataFusion2/den_fusion/2014-01-01-01_120.0_10.0_10.0_fusion.png
                for (String s : split) {
                    if (StringUtils.hasText(s)) {
                        res.add(s);
                    }
                }
            }
        }
        String zipPath = PathUtil.tmpDir() + "/" + System.currentTimeMillis() + ".zip";
        File[] fileArr = (File[])res.stream().map(a -> new File(a)).toArray();
        File zip = ZipUtil.zip(new File(zipPath), false, fileArr);
        return zip;
    }

    /**
     * 融合计算
     * @param param
     * @return
     */
    public List<DensityFustionVO> fusionCalc(DensityFusionParam param) throws ProcessStopException, ServerException {
        ////找到参数下的文件，拼装 卫星 -文件 对应关系
        ////通过时间先找到 数据 ,再通过时次 筛数据
        //String filePath = param.getFilePath();
        //String interval = param.getInterval();
        //String[] grids = interval.split("°");
        //String timeStr = filePath.substring(0, filePath.lastIndexOf("-"));
        //LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        //qw.eq(ResultSave::getModule,DATA_FUSION);
        //qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);
        //qw.eq(ResultSave::getStartTime,timeStr);
        //List<ResultSave> pos = resultSaveService.list(qw);
        //List<String> targetFile = new ArrayList<>();
        //for (ResultSave po : pos) {
        //    ///dgdq/tmp/dataFusion/Highly_normalized/gird_txt/GRACEB_2013-11-29-02_2013-11-30-02_430_2013-11-29-14_10_10_latlon_grid.txt
        //    String resultData = po.getResultData();
        //    List<String> resList = JSONUtil.toList(resultData, String.class);
        //    for (String res : resList) {
        //        String name = res.substring(res.lastIndexOf("/")+1);
        //        if (name.endsWith("latlon_grid.txt")) {
        //            //只考虑这种文件
        //            String[] strs = name.split("_");
        //            if (filePath.equals(strs[4])) {
        //                //时次相同
        //                //截取卫星名字
        //                String state = name.substring(0,name.indexOf("_"));
        //                String grid = strs[5];
        //                if (param.getSatellites().contains(state) && grid.equals(grids[0])) {
        //                    //卫星相同 和分辨率 相同
        //                    targetFile.add(res);
        //                }
        //            }
        //        }
        //    }
        //}
        ////将文件按卫星划分  xxx/xxx/GRACEB_2013-11-29-02_2013-11-30-02_430_2013-11-29-14_10_10_latlon_grid.txt
        //Map<String, List<String>> collect = targetFile.stream().collect(Collectors.groupingBy(a -> {
        //    return a.substring(a.lastIndexOf("/") + 1).split("_")[0];
        //}));

        /**
         *     parser = argparse.ArgumentParser(description="数据融合计算")
         *     parser.add_argument("-sn", "--sat_name", type=str,
         *                         default="GRACEA,SWARMB", help="卫星名称，默认为 'SWARMA,SWARMB'")
         *     parser.add_argument("-alt", "--alts", type=str, default="120,600,10",
         *                         help="数据处理的高度范围及间隔")
         *     parser.add_argument("-filepath", "--file_path", type=str,
         *                         default="/dgdq/tmp/dataFusion2/gird_txt",
         *                         help="结果数据的存储绝对路径，格式为 'file.txt'")
         *     parser.add_argument("-outpath", "--out_path", type=str,
         *                         default="/dgdq/tmp/dataFusion2/den_fusion",
         *                         help="结果数据的存储绝对路径，格式为 'file.txt'")
         *     parser.add_argument("-centime", "--cen_time", type=str,
         *                         default="2014-01-01,2014-01-31,1,23,3",
         *                         help="融合计算时间格式为开始日期、结束日期、开始小时、结束小时'2013-11-30,2013-11-30,1,23,3'")
         *     parser.add_argument("-span", "--spans", type=str, default="10,10",
         *                         help="纬度、经度间隔，格式为 '10,10'")
         */

        String script = PathUtil.codeModuleDir(BizModule.DATA_FUSION) + PathUtil.moduleDirs().getDataFusion().get("denfusion");
        ArrayList<String> commonds = new ArrayList<>();
        commonds.add("python");
        commonds.add(script);
        commonds.add("-sn");
        commonds.add(CollectionUtil.join(param.getSatellites(),","));
        commonds.add("-alt");
        commonds.add(param.getHightMin()+","+param.getHightMax()+","+param.getHightGap());
        commonds.add("-filepath");
        commonds.add("/dgdq/tmp/dataFusion2/gird_txt");
        commonds.add("-outpath");
        commonds.add("/dgdq/tmp/dataFusion2/den_fusion");
        commonds.add("-centime");
        int startHour = DateUtil.hour(param.getStartTime(),true);
        int endHour = DateUtil.hour(param.getEndTime(),true);
        commonds.add(DateUtil.format(param.getStartTime(),"yyyy-MM-dd")+","+DateUtil.format(param.getEndTime(),"yyyy-MM-dd")
                +","+startHour+","+endHour+","+param.getDataGap());
        commonds.add("-span");
        commonds.add(param.getLatGap()+","+param.getLonGap());


        String execCommond = CollectionUtil.join(commonds, " ");
        log.info("执行命令:{}",execCommond);
        List<String> strings = ExecUtil.execPythonZw(commonds, "sseid");
        log.info("解析结果:{}", strings);
        //将结果缓存到redis
        String key = "FUSION_RES:"+param.toString();
        redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(strings),2, TimeUnit.HOURS);
        List<DensityFustionVO> res = new ArrayList<>();
        for (String line : strings) {
            if (line.startsWith("###")) {
                String[] split = line.split("['#,]+");
                ///dgdq/tmp/dataFusion2/den_fusion/2014-01-01-01_120.0_10.0_10.0_fusion.png
                for (String s : split) {
                    if (StringUtils.hasText(s)) {
                        DensityFustionVO vo = new DensityFustionVO();
                        vo.setPng(s);
                        String name = s.substring(s.lastIndexOf("/") + 1);
                        String[] namesplit = name.split("_");
                        vo.setHight(Float.valueOf(namesplit[1]).intValue());
                        vo.setSatellites(param.getSatellites());
                        vo.setFusionDate(namesplit[0]);
                        res.add(vo);
                    }
                }
            }
        }
        if (res.size()==0) {
            throw new ServiceException(strings.toString());
        }
        return res;
    }


    /**
     * 返回文件包含的卫星列表
     *
     * @param filePath
     * @return
     */
    public DensityFustionSatelliteVO satelliteByFile(String filePath) {
        //2013-11-29-14
        //通过时间先找到 数据 ,再通过时次 筛数据
        String timeStr = filePath.substring(0, filePath.lastIndexOf("-"));
        LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        qw.eq(ResultSave::getModule,DATA_FUSION);
        qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);
        qw.eq(ResultSave::getStartTime,timeStr);
        List<ResultSave> pos = resultSaveService.list(qw);
        Set<CommonVO> satellites = new HashSet<>();
        Set<String> grids  = new HashSet<>();
        for (ResultSave po : pos) {
            ///dgdq/tmp/dataFusion/Highly_normalized/gird_txt/GRACEB_2013-11-29-02_2013-11-30-02_430_2013-11-29-14_10_10_latlon_grid.txt
            String resultData = po.getResultData();
            List<String> resList = JSONUtil.toList(resultData, String.class);
            for (String res : resList) {
                String name = res.substring(res.lastIndexOf("/")+1);
                if (name.endsWith("latlon_grid.txt")) {
                    //只考虑这种文件
                    String[] strs = name.split("_");
                    if (filePath.equals(strs[4])) {
                        //时间及时次相同
                        //截取卫星名字
                        String state = name.substring(0,name.indexOf("_"));
                        satellites.add(new CommonVO(state,state));
                        grids.add(strs[5]+"°*"+strs[6]+"°");
                    }
                }
            }
        }
        return new DensityFustionSatelliteVO(satellites,grids);
    }



    /**
     * 返回 指定时次的卫星列表
     *
     * @param filePath
     * @return
     */
    public List<CommonVO> satellitesByTime(String filePath) throws ServerException {
        //2013-11-29-14
        //通过时间先找到 数据 ,再通过时次 筛数据
        String timeStr = filePath.substring(0, filePath.lastIndexOf("-"));
        LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        qw.eq(ResultSave::getModule,DATA_FUSION);
        qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);
        qw.eq(ResultSave::getStartTime,timeStr);
        List<ResultSave> pos = resultSaveService.list(qw);

        //1 获取该时次下 所有的卫星
        Set<String> sates = new TreeSet<>();
        for (ResultSave po : pos) {
            ///dgdq/tmp/dataFusion/Highly_normalized/gird_txt/GRACEB_2013-11-29-02_2013-11-30-02_430_2013-11-29-14_10_10_latlon_grid.txt
            String resultData = po.getResultData();
            List<String> resList = JSONUtil.toList(resultData, String.class);
            for (String res : resList) {
                String name = res.substring(res.lastIndexOf("/")+1);
                if (name.endsWith("latlon_grid.txt")) {
                    //只考虑这种文件
                    String[] strs = name.split("_");
                    if (filePath.equals(strs[4])) {
                        //时间及时次相同
                        //截取卫星名字
                        String state = name.substring(0,name.indexOf("_"));
                        sates.add(state);
                        //一条数据只有一个卫星
                        break;
                    }
                }
            }
        }
        int sateSize = sates.size();
        if(sateSize <2) {
            throw new ServiceException("该时次下数据不足2颗卫星的数据，请更换时次或先去进行经纬度格网化。");
        }
        List<CommonVO> resList = new ArrayList<>();
        for (String sate : sates) {
            resList.add(new CommonVO(sate,sate));
        }
        return resList;
    }

    /**
     * 返回 指定时次的多个卫星的  相同分辨率
     *
     * @param filePath 文件时次
     * @param satellites 指定卫星
     * @return
     */
    public Set<String> gridBySate(String filePath,List<String> satellites) throws ServerException {
        if (satellites.size() < 2) {
            throw new ServiceException("请选择多颗卫星的经纬度格网化数据。");
        }

        //2013-11-29-14
        //通过时间先找到 数据 ,再通过时次 筛数据
        String timeStr = filePath.substring(0, filePath.lastIndexOf("-"));
        LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        qw.eq(ResultSave::getModule,DATA_FUSION);
        qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);
        qw.eq(ResultSave::getStartTime,timeStr);
        List<ResultSave> pos = resultSaveService.list(qw);

        //1 获取该时次下 所有的卫星 及相应的 分辨率

        Map<String, Integer> gridMap = new HashMap<>();
        for (ResultSave po : pos) {
            ///dgdq/tmp/dataFusion/Highly_normalized/gird_txt/GRACEB_2013-11-29-02_2013-11-30-02_430_2013-11-29-14_10_10_latlon_grid.txt
            String resultData = po.getResultData();
            List<String> resList = JSONUtil.toList(resultData, String.class);
            for (String res : resList) {
                String name = res.substring(res.lastIndexOf("/")+1);
                if (name.endsWith("latlon_grid.txt")) {
                    //只考虑这种文件
                    String[] strs = name.split("_");
                    if (filePath.equals(strs[4])) {
                        //时间及时次相同
                        //截取卫星名字
                        String state = name.substring(0,name.indexOf("_"));
                        if (satellites.contains(state)) {
                            //储存分辨率的频数
                            String grid = strs[5] + "°*" + strs[6] + "°";
                            Integer integer = gridMap.get(grid);
                            if (null == integer) {
                                gridMap.put(grid, 1);
                            } else {
                                gridMap.put(grid, ++integer);
                            }
                            //一条数据只有一个分辨率
                            break;
                        }
                    }
                }
            }
        }
        //2 取 卫星分辨率的交集.若无则提示重新选数据
        int size = satellites.size();
        Set<String> keySet = gridMap.keySet();
        Set<String> grids = new HashSet<>();
        for (String key : keySet) {
            if (size == gridMap.get(key)) {
                //个数相同,说明有交集
                grids.add(key);
            }
        }
        if (grids.size() == 0) {
            throw new ServiceException("该时次下卫星无相同的分辨率数据，请更换时次或先去进行经纬度格网化。");
        }
        return grids;
    }

    /**
     * 通过是时间返回有数据
     * @return
     */
    public List<String> dateList(DensityFusionParam param) {
        LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        qw.eq(ResultSave::getModule,DATA_FUSION);
        qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);

        //监测是否有该年、月的数据
        qw.le(ResultSave::getStartTime,DateUtil.format(param.getStartTime(),"yyyy-MM-dd HH:mm:ss"));
        qw.ge(ResultSave::getEndTime,DateUtil.format(param.getEndTime(),"yyyy-MM-dd HH:mm:ss"));
        List<ResultSave> pos = resultSaveService.list(qw);
        if (CollectionUtil.isNotEmpty(pos)) {
            Set<String> res = new HashSet<>();
            for (ResultSave po : pos) {
                //判断有无数据间隔  dataGap::::10
                String params = po.getParams();
                String[] split = params.split(Constants.FIELD_D);
                boolean isgo  = false;
                for (String s : split) {
                    if (s.startsWith("dataGap")) {
                        if (Integer.valueOf(s.split(Constants.ENTRY_D)[1]).compareTo(param.getDataGap()) == 0 ) {
                            //数据间隔相等
                            isgo = true;
                            break;
                        }
                    }
                }
                if (isgo) {
                    String resultData = po.getResultData();
                    List<String> datas = JSONUtil.toList(resultData, String.class);
                    for (String dd : datas) {
                        //"GRACEB_2013-11-29-02_2013-11-30-02_470_2013-11-29-14_time_normalized.txt"
                        String name = dd.substring(dd.lastIndexOf("/") + 1);
                        ///dgdq/tmp/dataFusion2/gird_txt/GRACEA_2013-12-31-13_2014-01-01-13_70.0_2014-01-01-01_10.0_10.0_latlon_grid.txt
                        if (name.endsWith("latlon_grid.txt")) {
                            //只考虑这种文件
                            String[] s = name.split("_");
                            res.add(s[0]);
                        }
                    }
                }
            }
            List<String> collect = res.stream().sorted().collect(Collectors.toList());
            return collect;
        }
        return ListUtil.empty();
    }


    /**
     * 查询是否有数据;通过年 返回月;通过年月返回日
     * @param param
     * @return
     */
    public List<Integer> findTime(DensityFusionParam param) {

        LambdaQueryWrapper<ResultSave> qw = new LambdaQueryWrapper();
        qw.select(ResultSave::getStartTime);
        qw.eq(ResultSave::getModule,DATA_FUSION);
        qw.eq(ResultSave::getSubmodule,DATA_FUSION_GRID);

        //监测是否有该年、月的数据
        qw.le(ResultSave::getStartTime,param.getStartTime());
        qw.ge(ResultSave::getStartTime,param.getEndTime());
        List<ResultSave> list = resultSaveService.list(qw);
        if (null != list) {
            HashSet<Integer> objects = new HashSet<>();
            for (ResultSave resultSave : list) {
                LocalDate parse = LocalDate.parse(resultSave.getStartTime(), DateTimeFormatter.ISO_LOCAL_DATE);
                objects.add(parse.getDayOfMonth());
            }
            List<Integer> collect = objects.stream().collect(Collectors.toList());
            return collect;
        }
        return ListUtil.empty();
    }

    /**
     * 结果保存
     * @param param
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean resSave(DensityFusionParam param) throws Exception {
        String key = "FUSION_RES:"+param.toString();
        String cachStr = (String)redisTemplate.opsForValue().get(key);
        if (null == cachStr) {
            this.fusionCalc(param);
            cachStr = (String)redisTemplate.opsForValue().get(key);
        }
        List<String> strings = JSONUtil.toList(cachStr, String.class);
        List<String> resList = new ArrayList<>();
        for (String line : strings) {
            if (line.startsWith("###")) {
                String[] split = line.split("['#,]+");
                ///dgdq/tmp/dataFusion2/den_fusion/2014-01-01-01_120.0_10.0_10.0_fusion.png
                for (String s : split) {
                    if (StringUtils.hasText(s)) {
                        resList.add(s);
                    }
                }
            }
        }

        String res = JSON.toJSONString(resList);
        Date  now = new Date();
        ResultSave entity = ResultSave.builder()
                .params("")
                .module(DATA_FUSION)
                .submodule(ResultSaveModule.DATA_FUSION_DENSITY)
                .resultData(res)
                .startTime(DateUtil.format(param.getStartTime(),"yyyy-MM-dd HH:mm:ss"))
                .endTime(DateUtil.format(param.getEndTime(),"yyyy-MM-dd HH:mm:ss"))
                .createTime(now)
                .build();
        resultSaveService.resultSave(param,entity);

        ////存入融合数据表  算法自行录入了
        ////2010-01-01-01_200_10_10_fusion.txt
        //String[] pathSplits = new File(path).getName().split("_");
        //FusionSatelliteRel fusionSatelliteRel = new FusionSatelliteRel();
        //fusionSatelliteRel.setSatellites(CollectionUtil.join(param.getSatellites(),","));
        //DateTime parse = DateUtil.parse(pathSplits[0], "yyyy-MM-dd-HH");
        //fusionSatelliteRel.setFustionTime(parse);
        //fusionSatelliteRel.setAlt(Integer.valueOf(pathSplits[1]));
        //fusionSatelliteRel.setIntervel(pathSplits[2]);
        //fusionSatelliteRel.setCreateTime(now);
        //LambdaQueryWrapper<FusionSatelliteRel> queryWrapper = new LambdaQueryWrapper<>();
        //queryWrapper.eq(FusionSatelliteRel::getSatellites,fusionSatelliteRel.getSatellites());
        //queryWrapper.eq(FusionSatelliteRel::getAlt,fusionSatelliteRel.getAlt());
        //queryWrapper.eq(FusionSatelliteRel::getIntervel,fusionSatelliteRel.getIntervel());
        //queryWrapper.eq(FusionSatelliteRel::getFustionTime,fusionSatelliteRel.getFustionTime());
        //long count = relService.count(queryWrapper);
        //if (count != 0) {
        //    //重复数据不再录入
        //    return true;
        //}
        //relService.save(fusionSatelliteRel);
        //Long id = fusionSatelliteRel.getId();
        //BufferedReader reader = new BufferedReader(new FileReader(path));
        ////忽略首行
        //reader.readLine();
        //String line = "";
        //List<FusionDensDataPO> pos = new ArrayList<>();
        //while ((line = reader.readLine()) != null) {
        //    //2010-01-01 00:59:52,-170.0,-90.0,200,3.94361379274233e-12,
        //    //time,longitude ,latitude,altitude,density
        //    String[] lineSplit = line.split(",");
        //    FusionDensDataPO po = new FusionDensDataPO();
        //    po.setLon(Float.valueOf(lineSplit[1]));
        //    po.setLat(Float.valueOf(lineSplit[2]));
        //    po.setAlt(Float.valueOf(lineSplit[3]));
        //    po.setDensity(new BigDecimal(lineSplit[4]));
        //    po.setSatellites(CollectionUtil.join(param.getSatellites(),","));
        //    Date date = DateUtils.parseDate(lineSplit[0], "yyyy-MM-dd HH:mm:ss");
        //    po.setFustionTime(date);
        //    po.setIntervel(pathSplits[2]+","+pathSplits[2]);
        //    pos.add(po);
        //}
        //fusionService.saveBatch(pos);
        return true;
    }
}
